package com.example.haikangdemo.service.impl;

import java.io.File;
import java.io.IOException;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.example.haikangdemo.configuration.HCNetSDK;
import com.example.haikangdemo.configuration.HaiKangProperties;
import com.example.haikangdemo.configuration.osSelect;
import com.example.haikangdemo.service.HaiKangInitService;
import com.example.haikangdemo.service.HaiKangListenService;
import com.sun.jna.Native;
import com.sun.jna.Pointer;

import lombok.extern.slf4j.Slf4j;

/**
 * @author HuangLongFei
 * @since 2023/09/23
 **/
@Slf4j
@Service
public class HaiKangInitServiceImpl implements HaiKangInitService {

    @Autowired
    private HaiKangListenService haiKangListenService;
    @Autowired
    private HaiKangProperties properties;

    public static HCNetSDK hCNetSDK = null;
    //    public static int lAlarmHandle = -1;//报警布防句柄
    //    public static int lListenHandle = -1;//报警监听句柄
    public static HCNetSDK.FMSGCallBack_V31 fmsgCallBack_v31;//设置报警回调函数

    public static FExceptionCallBack_Imp fExceptionCallBack;

    /**
     * win系统和Linux系统，动态库加载
     */
    private boolean CreateSDKInstance() {
        if (hCNetSDK == null) {
            synchronized (HCNetSDK.class) {
                String strDllPath = "";
                try {
                    if (osSelect.isWindows()) {
                        //win系统加载库路径
                        strDllPath = System.getProperty("user.dir")
                            + "\\src\\main\\resources\\lib\\hikvision\\win64\\HCNetSDK.dll";
                    } else if (osSelect.isLinux()) {
                        //Linux系统加载库路径
                        strDllPath = System.getProperty("user.dir") + properties.getLibPath() + File.separator
                            + "libhcnetsdk.so";
                    }
                    hCNetSDK = (HCNetSDK) Native.loadLibrary(strDllPath, HCNetSDK.class);
                } catch (Exception ex) {
                    log.error("loadLibrary: " + strDllPath + " Error: " + ex.getMessage());
                    return false;
                }
            }
        }
        return true;
    }

    @Override
    @PostConstruct
    public void initSDKTools() {
        if (hCNetSDK == null) {
            // 动态库加载
            if (!CreateSDKInstance()) {
                log.error("load HaiKang SDK fail");
                return;
            }
        }
        //linux系统建议调用以下接口加载组件库
        if (osSelect.isLinux()) {
            HCNetSDK.BYTE_ARRAY ptrByteArray1 = new HCNetSDK.BYTE_ARRAY(256);
            HCNetSDK.BYTE_ARRAY ptrByteArray2 = new HCNetSDK.BYTE_ARRAY(256);
            //这里是库的绝对路径，请根据实际情况修改，注意改路径必须有访问权限
            String strPath1 = System.getProperty("user.dir") + properties.getLibPath() + File.separator
                + "libcrypto.so.1.1";
            String strPath2 = System.getProperty("user.dir") + properties.getLibPath() + File.separator
                + "libssl.so.1.1";

            System.arraycopy(strPath1.getBytes(), 0, ptrByteArray1.byValue, 0, strPath1.length());
            ptrByteArray1.write();
            hCNetSDK.NET_DVR_SetSDKInitCfg(3, ptrByteArray1.getPointer());

            System.arraycopy(strPath2.getBytes(), 0, ptrByteArray2.byValue, 0, strPath2.length());
            ptrByteArray2.write();
            hCNetSDK.NET_DVR_SetSDKInitCfg(4, ptrByteArray2.getPointer());

            String strPathCom = System.getProperty("user.dir");
            HCNetSDK.NET_DVR_LOCAL_SDK_PATH struComPath = new HCNetSDK.NET_DVR_LOCAL_SDK_PATH();
            System.arraycopy(strPathCom.getBytes(), 0, struComPath.sPath, 0, strPathCom.length());
            struComPath.write();
            hCNetSDK.NET_DVR_SetSDKInitCfg(2, struComPath.getPointer());
        }
        //SDK初始化，一个程序只需要调用一次
        boolean initSuc = hCNetSDK.NET_DVR_Init();
        if (!initSuc) {
            log.error("初始化失败........................");
        }
        // 设置连接时间与重连时间
        hCNetSDK.NET_DVR_SetConnectTime(2000, 1);
        hCNetSDK.NET_DVR_SetReconnect(10000, true);

        //启动SDK写日志
        hCNetSDK.NET_DVR_SetLogToFile(3, "..\\sdkLog\\", false);

        //异常消息回调
        if (fExceptionCallBack == null) {
            fExceptionCallBack = new FExceptionCallBack_Imp();
        }

        Pointer pUser = null;
        //设置异常回调函数(可在回调函数中获取设备上下线状态等)
        if (!hCNetSDK.NET_DVR_SetExceptionCallBack_V30(0, 0, fExceptionCallBack, pUser)) {
            return;
        }
        log.info("设置异常消息回调成功");

        //设置报警回调函数
        fmsgCallBack_v31 = new FMSGCallBack1();
        boolean b = hCNetSDK.NET_DVR_SetDVRMessageCallBack_V31(fmsgCallBack_v31, null);
        //如果设置报警回调失败，获取错误码
        if (!b) {
            log.error("SetDVRMessageCallBack failed, error code=" + hCNetSDK.NET_DVR_GetLastError());
        }

        //    设备上传的报警信息是COMM_VCA_ALARM(0x4993)类型，
        // 在SDK初始化之后增加调用NET_DVR_SetSDKLocalCfg(enumType为NET_DVR_LOCAL_CFG_TYPE_GENERAL)设置通用参数NET_DVR_LOCAL_GENERAL_CFG的byAlarmJsonPictureSeparate为1，
        // 将Json数据和图片数据分离上传，这样设置之后，报警布防回调函数里面接收到的报警信息类型为COMM_ISAPI_ALARM(0x6009)，
        // 报警信息结构体为NET_DVR_ALARM_ISAPI_INFO（与设备无关，SDK封装的数据结构），更便于解析。
        HCNetSDK.NET_DVR_LOCAL_GENERAL_CFG struNET_DVR_LOCAL_GENERAL_CFG = new HCNetSDK.NET_DVR_LOCAL_GENERAL_CFG();
        struNET_DVR_LOCAL_GENERAL_CFG.byAlarmJsonPictureSeparate = 1;   //设置JSON透传报警数据和图片分离
        struNET_DVR_LOCAL_GENERAL_CFG.write();
        Pointer pStrNET_DVR_LOCAL_GENERAL_CFG = struNET_DVR_LOCAL_GENERAL_CFG.getPointer();
        hCNetSDK.NET_DVR_SetSDKLocalCfg(17, pStrNET_DVR_LOCAL_GENERAL_CFG);
        // 登录设备
        int userId = haiKangListenService.deviceRegister(properties.getHost(), properties.getPort(),
            properties.getUsername(), properties.getPassword());
        // 布放
        haiKangListenService.placement(userId);
    }

    /**
     * 获取异常信息
     */
    public static class FExceptionCallBack_Imp implements HCNetSDK.FExceptionCallBack {
        @Override
        public void invoke(int dwType, int lUserID, int lHandle, Pointer pUser) {
            System.out.println("异常事件类型:" + dwType);
            return;
        }
    }

    /**
     * 报警消息回调
     */
    public class FMSGCallBack1 implements HCNetSDK.FMSGCallBack_V31 {
        //lCommand 上传消息类型  pAlarmer 报警设备信息  pAlarmInfo  报警信息   dwBufLen 报警信息缓存大小   pUser  用户数据
        //报警信息回调函数
        @Override
        public boolean invoke(int lCommand, HCNetSDK.NET_DVR_ALARMER pAlarmer, Pointer pAlarmInfo, int dwBufLen,
            Pointer pUser) throws IOException {
            haiKangListenService.alarmDataHandle(lCommand, pAlarmer, pAlarmInfo, dwBufLen, pUser);
            return true;
        }
    }
}
